package fourmiefight.ihm;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferStrategy;

import org.dyn4j.collision.manifold.Manifold;
import org.dyn4j.collision.narrowphase.Penetration;
import org.dyn4j.dynamics.Body;
import org.dyn4j.dynamics.BodyFixture;
import org.dyn4j.dynamics.CollisionListener;
import org.dyn4j.dynamics.World;
import org.dyn4j.dynamics.contact.ContactConstraint;
import org.dyn4j.geometry.Vector2;

import fourmiefight.abstractionlib2d.CustomBody;
import fourmiefight.abstractionlib2d.MyBounds;
import fourmiefight.controller.Simulateur;
import fourmiefight.controller.Simulateur.State;
import fourmiefight.metier.Carte;
import fourmiefight.metier.Food;
import fourmiefight.metier.Fourmi;
import fourmiefight.metier.Fourmiliere;
import fourmiefight.metier.Pheromone;
import fourmiefight.metier.UpdateTimer;
import fourmiefight.tools.Signals;

public class SimulateurIHM implements MouseListener, CollisionListener
{
	public static final double SCALE = 45.0;
	private static final int WIDTH = 600;
	private static final int HEIGHT = 600;
	private static final MyBounds BOUNDS = new MyBounds((double)WIDTH, (double)HEIGHT );

	private Carte carte;
	private UpdateTimer timer;
	private Canvas canvas;
	private World world;
	private Simulateur simu;
	
	private PopUpCarte popUp;
	
	public SimulateurIHM(Simulateur simu) 
	{
        createCanvas();
        
        this.simu = simu;
		
		world = new World(BOUNDS);
		world.setGravity(World.ZERO_GRAVITY);
		world.addListener(this); // TODO externaliser le listener?
		world.addBody(BOUNDS.b1);
		world.addBody(BOUNDS.b2);
		world.addBody(BOUNDS.b3);
		world.addBody(BOUNDS.b4);
	}

	private void createCanvas() {
		Dimension size = new Dimension(WIDTH, HEIGHT);
		this.canvas = new Canvas();
        this.canvas.setPreferredSize(size);
        this.canvas.setMinimumSize(size);
        this.canvas.setMaximumSize(size);
        canvas.addMouseListener(this);
	}
	
	/**
     * The method calling the necessary methods to update
     * the game, graphics, and poll for input.
     */
    public void gameLoop() 
    {
        Graphics2D g = (Graphics2D) this.canvas.getBufferStrategy().getDrawGraphics();
       
        AffineTransform yFlip = AffineTransform.getScaleInstance(1, -1);
        AffineTransform move = AffineTransform.getTranslateInstance(WIDTH / 2, - HEIGHT / 2);
        g.transform(yFlip);
        g.transform(move);
       
        this.render(g);
        g.dispose();
       
        BufferStrategy strategy = this.canvas.getBufferStrategy();
        if (!strategy.contentsLost()) 
        {
                strategy.show();
        }
           
	    Toolkit.getDefaultToolkit().sync();
	    this.world.update(timer.getElapsedTime());
    }
	
	/**
     * Renders the example.
     * @param g the graphics object to render to
     */
    protected void render(Graphics2D g) 
    {
        g.setColor(Color.WHITE);
        g.fillRect(-WIDTH/2, -HEIGHT/2, WIDTH, HEIGHT);
       
        // Rends tous les éléments du world
        for (int i = 0; i < this.world.getBodyCount(); i++) 
        {
            Body go = this.world.getBody(i);
            if(go instanceof CustomBody)
            {
            	((CustomBody)go).getView().render(g, go);
            }
        }
    }
	
	@Override
	public boolean collision(Body b1, BodyFixture arg1, Body b2,
			BodyFixture arg3, Penetration arg4) 
	{
		boolean bd1 = b1 instanceof Fourmi;
		boolean bd2 = b2 instanceof Fourmi;
		
		boolean food1 = b1 instanceof Food;
		boolean food2 = b2 instanceof Food;
		
		boolean fourmiliere1 = b1 instanceof Fourmiliere;
		boolean fourmiliere2 = b2 instanceof Fourmiliere;
		
		boolean ph1 = b1 instanceof Pheromone;
		boolean ph2 = b2 instanceof Pheromone;
		
		if(bd2 && food1)
		{
			updateSignalBouffe(b1, b2);
			System.out.println("Collision nourriture");
		}
		else if(bd1 && food2)
		{
			updateSignalBouffe(b2, b1);
			System.out.println("Collision nourriture");
		}
		else if(bd1 && fourmiliere2)
		{
			updateSignalFourmilliere(b1, b2);
		}
		else if(bd2 && fourmiliere1)
		{
			updateSignalFourmilliere(b2, b1);
		}
		else if(bd1 && ph2)
		{
			updateSignalPheromone(b1, b2);
		}
		else if(bd2 && ph1)
		{
			updateSignalPheromone(b2, b1);
		}
		else
		{
			if(bd1)
			{
				
			}
			
			if(bd2)
			{
				
			}
		}
		
		return true;
	}

	private void updateSignalFourmilliere(Body fourmi, Body fourmilliere) 
	{
		int food = ((Fourmi) fourmi).getNourritureTransportee();
		if(((Fourmi) fourmi).updateState(Signals.COLLISION_FOURMILLIERE, ((Fourmiliere) fourmilliere).getIdd()))
		{
			((Fourmiliere)fourmilliere).addFood(food);
		}
	}

	private void updateSignalBouffe(Body food, Body fourmi) 
	{
		if(((Fourmi) fourmi).updateState(Signals.COLLISION_FOOD))
		{
			Food food2 = (Food) food;
			((Fourmi) fourmi).seServirEnNourriture(food2);
		}
	}
	
	private void updateSignalPheromone(Body fourmi, Body pheromone)
	{
		if(((Fourmi) fourmi).updateState(Signals.COLLISION_PHEROMONES, ((Pheromone)pheromone).getIdFourmiliere()))
		{
			Pheromone ph = (Pheromone) pheromone;
			((Fourmi) fourmi).suivrePheromone(ph);
		}
	}

	@Override
	public boolean collision(ContactConstraint cc) 
	{
		return true;
	}

	@Override
	public boolean collision(Body b1, Body b2) 
	{
		return true;
	}

	@Override
	public boolean collision(Body b1, BodyFixture arg1, Body b2,
			BodyFixture arg3, Manifold arg4) 
	{
		return false;
	}
	
	@Override
	public void mouseClicked(MouseEvent clic) {
		
		Object objToAdd = null;

		switch(clic.getButton()) 
		{
		case MouseEvent.BUTTON1 : 
			// ajouterFourmiliere(clic.getX(), clic.getY());
			 break;
				 
		case MouseEvent.BUTTON3 : 
			if (this.simu.getEtatSimulateur().equals(State.ARRET)) {
				popUp = new PopUpCarte(this, new Point(clic.getX(), clic.getY()));
				popUp.show(this.canvas, this.canvas.getMousePosition().x, this.canvas.getMousePosition().y);
			}
			break;
		}
	}

	@Override
	public void mouseEntered(MouseEvent arg0) {
	}

	@Override
	public void mouseExited(MouseEvent arg0) {
	}

	@Override
	public void mousePressed(MouseEvent arg0) {
	}

	@Override
	public void mouseReleased(MouseEvent arg0) {
	}

	public static int getWidth() {
		return WIDTH;
	}

	public static int getHeight() {
		return HEIGHT;
	}
	
	public Canvas getCanvas() {
		return canvas;
	}
	
	public void setCarte(Carte map) {
		carte = map;
	}

	public void setTimer(UpdateTimer timer) {
		this.timer = timer;
	}
	
	public void ajouterFourmiliere(double x, double y) 
	{
		carte.ajouterFourmiliere(80);
		int position = carte.getListeFourmiliere().size() - 1;
		Fourmiliere f = carte.getListeFourmiliere().get(position);
		f.translate((x - WIDTH / 2) / 45, (- (y - HEIGHT / 2)) / 45);
		world.addBody(f); 
	}
	
	public void ajouterNourriture(double x, double y) 
	{
		carte.ajouterFood(800);
		int position = carte.getListeFood().size() - 1;
		Food f = carte.getListeFood().get(position);
		f.translate((x - WIDTH / 2) / 45, (- (y - HEIGHT / 2)) / 45);
		world.addBody(f);
	}
	
	public void lancerSimu()
	{
		for (Fourmiliere f : this.carte.getListeFourmiliere()) 
		{
			for (Fourmi fourmi : f.getListeFourmis()) 
			{
				fourmi.translate(f.getWorldCenter().x,
					f.getWorldCenter().y);
				world.addBody(fourmi);
			}
		}
	}
	
	public void pauseSimu()
	{
		
	}
	
	public void stopSimu() 
	{
		System.out.println(world.getBodyCount());
		for (int i = 0; i < world.getBodyCount(); i++) 
        {
            Body go = world.getBody(i);
            if(go instanceof Fourmi)
            {
            	
            	world.removeBody(go);
            }
        }
		System.out.println(world.getBodyCount());
	}
	
	public void resetSimu() 
	{
		canvas.repaint();
	}
	
    public static double getAngle(Vector2 position1, Vector2 position2)
    {
        return Math.atan2(position2.y - position1.y, position2.x - position1.x);
    }
}
